﻿using System;
using UnityEngine;
using UnityEngine.EventSystems;

/// <summary>拖拽窗口边缘限制</summary>
/// <remarks>
/// <para>使用方式：</para>
/// <list type="number">
///     <item>将该脚本挂载到任意可以检测拖拽事件的2D元素上</item>
///     <item>可以调整【<see cref="margin"/>】属性来控制屏幕内保留的像素区域</item>
///     <item>注意：属性【<see cref="panelRectTransform"/>】是指在拖拽时要改变位置的UI元素</item>
/// </list>
/// </remarks>
public class DragWindowEdgeLimit : MonoBehaviour, IBeginDragHandler, IDragHandler
{
    [SerializeField]
    private RectTransform panelRectTransform;
    [SerializeField]
    private Margin margin;
    private RectTransform referRectTransform;
    private bool clampedToLeft, clampedToRight, clampedToTop, clampedToBottom;
    private Vector3[] canvasCorners;
    private Vector4 compareValuesX, compareValuesY;
    private float winMaxPos_X, winMinPos_X, winMaxPos_Y, winMinPos_Y;
    private float winMaxDrag_X, winMinDrag_X, winMaxDrag_Y, winMinDrag_Y;
    private float m_Rate;
    private int m_LastScreenWidth, m_LastScreenHeight;

    private void OnEnable()
    {
        if (referRectTransform == null) return;
        CalculateAnyVlaue();
    }

    private void Update()
    {
        if (referRectTransform == null) return;
        if (m_LastScreenWidth != Screen.width || m_LastScreenHeight != Screen.height)
            CalculateAnyVlaue();
    }

    private void SetAnyDirectionBoolValue(Vector4 compareValues, float max, float min, ref bool isClamp1, ref bool isClamp2)
    {
        if (compareValues.x > max)
        {
            if (!isClamp1) isClamp1 = true;
        }
        else if (isClamp1)
            isClamp1 = false;
        else if (compareValues.z < min)
        {
            if (!isClamp2) isClamp2 = true;
        }
        else if (isClamp2)
            isClamp2 = false;
    }

    private void CheckClampToWindow()
    {
        Vector3[] panelRectCorners = new Vector3[4];
        panelRectTransform.GetWorldCorners(panelRectCorners);
        compareValuesX.x = panelRectCorners[2].x * m_Rate;
        compareValuesX.z = panelRectCorners[0].x * m_Rate;
        compareValuesY.x = panelRectCorners[2].y * m_Rate;
        compareValuesY.z = panelRectCorners[0].y * m_Rate;
        //LogManager.Log($"curMinX = {compareValuesX.z}  curMaxX = {compareValuesX.x}  curMinY = {compareValuesY.z}  curMaxY = {compareValuesY.x}");
        SetAnyDirectionBoolValue(compareValuesX, winMaxDrag_X, winMinDrag_X, ref clampedToRight, ref clampedToLeft);
        SetAnyDirectionBoolValue(compareValuesY, winMaxDrag_Y, winMinDrag_Y, ref clampedToTop, ref clampedToBottom);
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        if (eventData.pointerId < -1 || !eventData.dragging) return;

        panelRectTransform.SetAsLastSibling();
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (eventData.pointerId < -1 || !eventData.dragging) return;

        Vector3 delta = eventData.delta;
        delta *= m_Rate;
        panelRectTransform.localPosition += delta;
        Vector2 clampedPosition = panelRectTransform.localPosition;
        CheckClampToWindow();
        if (clampedToRight)
        {
            clampedPosition.x = winMaxPos_X;
        }
        else if (clampedToLeft)
        {
            clampedPosition.x = winMinPos_X;
        }

        if (clampedToTop)
        {
            clampedPosition.y = winMaxPos_Y;
        }
        else if (clampedToBottom)
        {
            clampedPosition.y = winMinPos_Y;
        }
        panelRectTransform.localPosition = clampedPosition;
    }

    public void Init(RectTransform canvasRefer)
    {
        referRectTransform = canvasRefer;
        CalculateAnyVlaue();
    }

    public void CalculateAnyVlaue()
    {
        m_LastScreenWidth = Screen.width;
        m_LastScreenHeight = Screen.height;
        m_Rate = UIUtility.UIRate;
        canvasCorners = new Vector3[4];
        referRectTransform.GetWorldCorners(canvasCorners);
        compareValuesX = new Vector4(0, canvasCorners[2].x, 0, canvasCorners[0].x);
        compareValuesY = new Vector4(0, canvasCorners[2].y, 0, canvasCorners[0].y);

        winMaxPos_X = canvasCorners[2].x * (1 - referRectTransform.pivot.x) * m_Rate + panelRectTransform.rect.width * panelRectTransform.pivot.x - margin.right;
        winMinPos_X = -canvasCorners[2].x * referRectTransform.pivot.x * m_Rate - (panelRectTransform.rect.width * (1 - panelRectTransform.pivot.x) - margin.left);
        winMaxPos_Y = canvasCorners[2].y * (1 - referRectTransform.pivot.y) * m_Rate - panelRectTransform.rect.height * (1 - panelRectTransform.pivot.y) + margin.top;
        winMinPos_Y = -canvasCorners[2].y * referRectTransform.pivot.y * m_Rate - panelRectTransform.rect.height * (1 - panelRectTransform.pivot.y) + margin.buttom;

        winMaxDrag_X = canvasCorners[2].x * m_Rate + panelRectTransform.rect.width - margin.right;
        winMinDrag_X = -panelRectTransform.rect.width + margin.left;
        winMaxDrag_Y = canvasCorners[2].y * m_Rate + margin.top;
        winMinDrag_Y = -panelRectTransform.rect.height + margin.buttom;

        //LogManager.Log($"X_Max:{winMaxDrag_X}\u3000X_Min:{winMinDrag_X}\u3000Y_Max:{winMaxDrag_Y}\u3000Y_Min:{winMinDrag_Y}");
    }

    [Serializable]
    private class Margin
    {
        public float top = 0;
        public float buttom = 50;
        public float left = 120;
        public float right = 50;
    }
}